#ifndef NODE_H_
#define NODE_H_

#include "KeyNode.h"
#include "../Conf/Tamanios.h"
#include "../Externa/Clave.h"

class Node
{
	private:

		unsigned short espacioLibre;
		unsigned int refNodo;
		unsigned int nivel;
		unsigned int posicionEnArchivo;
		unsigned short tamanio;
		KeyNode* keyNode;

	public:

		Node(unsigned int refNodo, unsigned int nivel, Clave* clave, unsigned short tamanio);

		Node(unsigned int refNodo, unsigned int nivel, unsigned short tamanio);

		Node();

		~Node();

		bool hasOverflow();
		bool hasUnderflow();

		unsigned short getTamanioMinimo(){
			return (2*(this->getTamanioEspacioClaves())/3);

		}
		unsigned short getTamanioEspacioClaves();

		//Devuelve CodigoArbol::MODIFICADO - Insercion correcta
		//Devuelve	CodigoArbol::OVERFLOW - Sobreflujo de claves
		void insertarClave(Clave* &clave, char* codigo);

		 //Devuelve	CodigoArbol::MODIFICADO - Eliminacion llevada a cabo correctamente.
		 //Devuelve	CodigoArbol::UNDERFLOW - Subflujo de claves
		void eliminarClave(Clave* clave, char* codigo);

		void actualizarEspacioLibre(KeyNode* claves, bool insercion);
		void actualizarEspacioLibre(Clave* clave, bool insercion);
		void actualizarEspacioLibre();

		//Devuelve Codigo::MODIFICADO en codigo.
		bool reemplazarClave(Clave* claveVieja, Clave* claveNueva, char* codigo);

		//Devuelve la clave mas cercana a la claveBuscada (o la misma si se encuentra presente)
		Clave* buscar(Clave* claveBuscada);

		Clave* buscarSiguiente(Clave* claveBuscada);

		bool vacio() {
			return this->getKeyNode()->empty();
		}

		bool operator == (const Node &nodo) {
			return (this->getPosicionEnArchivo() == nodo.getPosicionEnArchivo());
		}

		unsigned short getCantidadClaves() {
			return this->getKeyNode()->size();
		}

		//Calcula el tamanio que ocupa el conjunto de claves dentro del nodo
		unsigned short getTamanioEnDiscoKeyNode();

		//Método que se encarga de ver cuantos bytes deberia ceder realmente el nodo para
		//poder cumplir con los 'bytesRequeridos'.
		//Si 'izquierda' es true, indica que el nodo debe ceder al hermano izquierdo,
		//en caso contrario, debe ceder al hermano derecho.
		//Si se le pide ceder mas de los bytes que tiene, devuleve cero. Sino, devuelve los
		//bytes a ceder.
		unsigned short bytesACeder(unsigned short bytesRequeridos, unsigned char &clavesPropuestas,
								   bool izquierda = true);

		//Como el anterior pero se fija si puede ceder cierta cantidad de claves en vez de bytes.
		unsigned short bytesACeder(unsigned char clavesPropuestas, bool izquierda = true);

		bool puedeRecibir(unsigned short bytesEntrantes, unsigned short bytesSalientes);

		unsigned short obtenerBytesRequeridos();

		//Devuelve la cantidad de bytes que sobran en un nodo luego de una inserción con overflow.
		//En 'cantClaves' devuelve la cantidad de claves que representan los bytes sobrantes.
		unsigned short obtenerBytesSobrantes(unsigned short &cantClaves, bool izquierda = true);

		unsigned short obtenerBytesSobreMinimo(bool izquierda = true);

		KeyNode* cederBytes(unsigned short bytesRequeridos, bool izquierda = true);

		KeyNode* cederClaves(unsigned short cantClaves, bool izquierda = true);

		void recibir(KeyNode* set);

		bool esPadre(Node* hijo, Clave* &claveProxNodo, Clave* claveNodoHijo);

		//Retorna un puntero la primera clave o NULL si el nodo no tiene claves.
		Clave* obtenerPrimeraClave();

		//Retorna un puntero la última clave o NULL si el nodo no tiene claves.
		Clave* obtenerUltimaClave();

		bool puedePasarClaveHaciaIzq(Node* nodoHnoIzq, Node* nodoPadre, Clave* clavePadre);

		bool puedePasarClaveHaciaDer(Node* nodoHnoDer, Node* nodoPadre, Clave* clavePadre);

		bool puedeRecibirClaveDesdeIzq(Node* nodoHnoIzq, Node* nodoPadre, Clave* clavePadre);

		bool puedeRecibirClaveDesdeDer(Node* nodoHnoDer, Node* nodoPadre, Clave* clavePadre);

		//Fusiona el conjunto de claves de este nodo con el conjunto de un nodo hermano y con una clave padre.
		void merge(Node* nodoHno, Clave* clavePadre);

		//Este metodo fusiona el conjunto de claves de este nodo con los conjuntos de dos nodos hermanos y con dos claves padre.
		void merge(Node* nodoHno1, Node* nodoHno2, Clave* clavePadre1, Clave* clavePadre2);

		Clave* extraerUltimaClave();

		Clave* extraerPrimeraClave();

		void extraerClave(Clave* clave);

		bool tieneOverflow();

		bool tieneUnderflow();

		//Split que se comporta como el de un arbol B (parte en dos el nodo), donde minClaves es
		//la cantidad (en bytes) que debe tener cada parte resultante.
		KeyNode* splitB(unsigned short minClaves);

		//Método que devuelve una copia de este nodo
		Node* copiar();

		//Método que carga en este nodo todos los mismos valores que posee el nodo pasado por parámetro
		Node& operator = (Node &nodo);


		void setRefNodo(unsigned int refNodo)
		{
			this->refNodo = refNodo;
		}

		void setHijoIzq(unsigned hijoIzq)
		{
			this->refNodo = hijoIzq;
		}

		void setHnoDer(unsigned hnoDer)
		{
			this->refNodo = hnoDer;
		}

		void setNivel(unsigned int nivel)
		{
			this->nivel = nivel;
		}

		void setKeyNode(KeyNode* keyNode);

		void setPosicionEnArchivo(unsigned int posicion)
		{
			this->posicionEnArchivo = posicion;
		}

		void setTamanio(unsigned short tamanio)
		{
			this->tamanio = tamanio;
		}

		unsigned int getRefNodo() const
		{
			return this->refNodo;
		}

		unsigned getHijoIzq() const
		{
			return this->refNodo;
		}

		unsigned getHnoDer() const
		{
			return this->refNodo;
		}

		unsigned int getNivel() const
		{
			return this->nivel;
		}

		unsigned int getPosicionEnArchivo() const
		{
			return this->posicionEnArchivo;
		}

		KeyNode* getKeyNode()
		{
			return this->keyNode;
		}

		unsigned char getTamanioHeader()
		{
			return (Tamanios::TAMANIO_ESPACIO_LIBRE + Tamanios::TAMANIO_NIVEL + Tamanios::TAMANIO_REFERENCIA);
		}

		unsigned short getTamanio() const
		{
			return this->tamanio;
		}

		unsigned short getEspacioLibre() const
		{
			return espacioLibre;
		}

		void setEspacioLibre(unsigned short espacio)
		{
			this->espacioLibre = espacio;
		}

};

#endif /*NODE_H_*/
